﻿#include "ssNetBase.h"
#include <cstdio>

namespace StarSeeker
{

//加载Socket库
bool LoadSocketLib()
{
#ifdef SS_USE_WINDOWS
	WSADATA wsaData;
	int nResult = WSAStartup(MAKEWORD(1,1), &wsaData);
	// 错误(一般都不可能出现)
    return NO_ERROR!=nResult;
#endif
    return true;
}

//卸载Socket库
void UnloadSocketLib()
{
#ifdef SS_USE_WINDOWS
	WSACleanup();
#endif
    return;
}

//获取错误代码
int GetSocketError()
{
#ifdef SS_USE_WINDOWS
	return WSAGetLastError();
#else
    return errno;
#endif
}

//关闭socket
void CloseOneSocket(SOCKET &s)
{
#ifdef SS_USE_WINDOWS
	closesocket(s);
#else
    shutdown(s, SHUT_RDWR);
    close(s);
#endif
	s = INVALID_SOCKET;
}

//创建socket
SOCKET CreateSocket()
{
#ifdef SS_USE_WINDOWS
    return socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#else
    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s<0)
        return INVALID_SOCKET;
    else
        return s;
#endif
}

SOCKET CreateSocketV6()
{
#ifdef SS_USE_WINDOWS
	return socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
#else
	SOCKET s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
	if (s<0)
		return INVALID_SOCKET;
	else
		return s;
#endif
}

//监听
int ListenPort(const char* ip, const unsigned short port, SOCKET &s, sockaddr_in* paddr)
{
	int ret = 0;
	s = CreateSocket();
	if (s == INVALID_SOCKET)
	{
		ret = GetSocketError();
		return ret;
	}

	sockaddr_in addr;
    addr.sin_family=AF_INET;
	//addr.sin_addr.s_addr=::inet_addr(szAddress);
	addr.sin_port=::htons(port);
	if (::inet_pton(PF_INET, ip, &addr.sin_addr) < 0)
	{
		ret = GetSocketError();
		CloseOneSocket(s);
		return ret;
	}
	if (paddr)
		*paddr = addr;

	if (::bind(s, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0)
	{
		ret = GetSocketError();
		CloseOneSocket(s);
		return ret;
	}

	if (::listen(s, 0) < 0)
	{
		ret = GetSocketError();
		CloseOneSocket(s);
		return ret;
	}
//#ifdef SS_USE_WINDOWS
//	linger ling;
//	ling.l_onoff = 0;
//	ling.l_linger = 1;
//	if(setsockopt(s, SOL_SOCKET, SO_LINGER, (char*)&ling, sizeof(ling)) < 0)
//	{
//		CloseOneSocket(s);
//		return INVALID_SOCKET;
//	}
//#endif
	return ret;
}

int ListenPortV6(const char * ip, const unsigned short port, SOCKET &s, sockaddr_in6* paddr)
{
	int ret = 0;
	s = CreateSocketV6();
	if (s == INVALID_SOCKET)
	{
		ret = GetSocketError();
		return ret;
	}

	sockaddr_in6 addr;
	memset(&addr, 0, sizeof(sockaddr_in6));
	addr.sin6_family = AF_INET6;
	addr.sin6_port = ::htons(port);
	if (::inet_pton(PF_INET6, ip, &addr.sin6_addr) < 0)
	{
		ret = GetSocketError();
		CloseOneSocket(s);
		return ret;
	}
	if (paddr)
		*paddr = addr;
//#ifdef SS_USE_WINDOWS
	//DWORD optval = 0;
	//::setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char*>(&optval), sizeof(optval));
//#endif
	if (::bind(s, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0)
	{
		ret = GetSocketError();
		CloseOneSocket(s);
		return ret;
	}

	if (::listen(s, 0) < 0)
	{
		ret = GetSocketError();
		CloseOneSocket(s);
		return ret;
	}
	return ret;
}

//连接
int ConnectToServer(const char *ip, const unsigned short port, SOCKET &s, sockaddr_in* paddr)
{
	int ret = 0;
    s = CreateSocket();
	sockaddr_in addrSrv;
	//addrSrv.sin_addr.S_un.S_addr=inet_addr(ip);
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=::htons(port);
	if (::inet_pton(PF_INET, ip, &(addrSrv.sin_addr)) < 0)
	{
		ret = GetSocketError();
		CloseOneSocket(s);
		return ret;
	}
	if (paddr)
		*paddr = addrSrv;
	if (::connect(s, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)) < 0)
	{
		ret = GetSocketError();
		CloseOneSocket(s);
	}
    return ret;
}

int ConnectToServerV6(const char * ip, const unsigned short port, SOCKET &s, sockaddr_in6* paddr)
{
	int ret = 0;
	s = CreateSocketV6();
	sockaddr_in6 addrSrv;
	memset(&addrSrv, 0, sizeof(sockaddr_in6));
	//addrSrv.sin_addr.S_un.S_addr=inet_addr(ip);
	addrSrv.sin6_family = AF_INET6;
	addrSrv.sin6_port = ::htons(port);
	if (::inet_pton(PF_INET6, ip, &(addrSrv.sin6_addr)) < 0)
	{
		ret = GetSocketError();
		CloseOneSocket(s);
		return ret;
	}
	if (paddr)
		*paddr = addrSrv;
	if (::connect(s, (sockaddr*)&addrSrv, sizeof(sockaddr_in6)) < 0)
	{
		ret = GetSocketError();
		CloseOneSocket(s);
	}
	return ret;
}

SOCKET AcceptListen(SOCKET listenSocket, sockaddr_in* paddr)
{
	struct sockaddr_in their_addr;
	socklen_t addrLen = sizeof(their_addr);
	SOCKET cs = ::accept(listenSocket, (sockaddr*)&their_addr, &addrLen);
	if (paddr)
		*paddr = their_addr;
	return cs;
}

SOCKET AcceptListenV6(SOCKET listenSocket, sockaddr_in6* paddr)
{
	struct sockaddr_in6 their_addr;
	socklen_t addrLen = sizeof(their_addr);
	SOCKET cs = ::accept(listenSocket, (sockaddr*)&their_addr, &addrLen);
	if (paddr)
		*paddr = their_addr;
	return cs;
}

// 获得本机的IP地址
void GetLocalIP(char *ip)
{
    char hostname[512] = {0};
#ifdef SS_USE_WINDOWS
	// 获得本机主机名
	gethostname(hostname, 512);
	struct hostent FAR* lpHostEnt = gethostbyname(hostname);
	if(lpHostEnt == NULL)
	{
		strcpy(ip, "127.0.0.1");
		return;
	}
	// 取得IP地址列表中的第一个为返回的IP(因为一台主机可能会绑定多个IP)
	LPSTR lpAddr = lpHostEnt->h_addr_list[0];
	// 将IP地址转化成字符串形式
	struct in_addr inAddr;
	memmove(&inAddr,lpAddr,4);
	inet_ntop(AF_INET, (void *)&inAddr, ip, 16);
#else
    gethostname(hostname, 512);
    hostent *hent = gethostbyname(hostname);
    if(hent == 0)
	{
		strcpy(ip, "127.0.0.1");
		return;
	}
    strcpy(ip, inet_ntoa(*(struct in_addr*)(hent->h_addr_list[0])));
#endif
}

//设置非阻塞socket
void Nonblock(SOCKET& s)
{
#ifdef SS_USE_WINDOWS
	unsigned long nb = 1;//0阻塞
	ioctlsocket(s, FIONBIO, &nb);
#else
    int flags;
    flags = fcntl(s, F_GETFL, 0);
    flags |= O_NONBLOCK;
    fcntl(s, F_SETFL, flags);
#endif
}

bool SetSendBuffer(SOCKET &s, unsigned int size)
{
	return setsockopt(s, SOL_SOCKET, SO_SNDBUF, (const char*)&size, sizeof(unsigned int)) == 0;
}

bool SetRecvBuffer(SOCKET &s, unsigned int size)
{
	return setsockopt(s, SOL_SOCKET, SO_RCVBUF, (const char*)&size, sizeof(unsigned int)) == 0;
}

bool SetKeepAlive(SOCKET &s, bool alive, int keepaliveinterval, int keepalivetime, int keepCount)
{
	if (s == INVALID_SOCKET)
	{
		return false;
	}
#ifdef SS_USE_WINDOWS
	ssTcpKeepalive inKeepAlive = { 0 };
	ssTcpKeepalive outKeepAlive = { 0 };
	inKeepAlive.onoff = alive ? 1 : 0;
	inKeepAlive.keepaliveinterval = keepaliveinterval;
	inKeepAlive.keepalivetime = keepalivetime;
	inKeepAlive.keepCount = keepCount;
	unsigned long ulBytesReturn = 0;
	return WSAIoctl(s, SIO_KEEPALIVE_VALS, (void*)&inKeepAlive, sizeof(ssTcpKeepalive), (void*)&outKeepAlive, sizeof(ssTcpKeepalive), &ulBytesReturn, NULL, NULL) == 0;
#else
	int keepAlive = alive ? 1 : 0;
	keepalivetime /= 1000;
	keepaliveinterval /= 1000;
	if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive)) == -1)
	{
		return false;
	}

	if (setsockopt(s, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&keepalivetime, sizeof(keepalivetime)) == -1)
	{
		return false;
	}

	if (setsockopt(s, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&keepaliveinterval, sizeof(keepaliveinterval)) == -1)
	{
		return false;
	}

	if (setsockopt(s, IPPROTO_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount)) == -1)
	{
		return false;
	}
	return true;
#endif
}

std::string DomainNameRes(const char * domainStr)
{
	if (IsIPv6Net())
	{
		struct addrinfo hint;
		memset(&hint, 0x0, sizeof(hint));
		hint.ai_family = AF_INET6;
		hint.ai_flags = AI_V4MAPPED;

		addrinfo* result = 0;
		getaddrinfo(domainStr, 0, &hint, &result);

		if (result != 0)
		{
			char hostname[1024] = { 0 };
			getnameinfo(result->ai_addr, result->ai_addrlen, hostname, 1024, 0, 0, 0);

			char ipv6[128] = { 0 };
			memcpy(ipv6, hostname, 128);

			freeaddrinfo(result);
			return ipv6;
		}
		else
		{
			freeaddrinfo(result);
		}
	}
	else
	{
		struct hostent* h = gethostbyname(domainStr);
		if (h != NULL)
		{
			unsigned char* p = (unsigned char *)(h->h_addr_list)[0];
			if (p != NULL)
			{
				char ip[16] = { 0 };
				sprintf(ip, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
				return ip;
			}
		}
	}
	return "";
}

bool IsIPv6Net(const char* domainStr)
{
	bool isIPV6Net = false;

	struct addrinfo* result = 0, *curr = 0;

	struct sockaddr_in6 dest;
	memset(&dest, 0, sizeof(dest));
	dest.sin6_family = AF_INET6;

	int ret = ::getaddrinfo(domainStr, 0, 0, &result);
	if (ret == 0)
	{
		for (curr = result; curr != 0; curr = curr->ai_next)
		{
			switch (curr->ai_family)
			{
			case AF_INET6:
				isIPV6Net = true;
				break;
			case AF_INET:
				break;
			default:
				break;
			}
		}
	}

	::freeaddrinfo(result);

	return isIPV6Net;
}

}
